import re
from OpenGL.GL import *
from errors import *
from flags import ContentFlags as C
from flags import SurfaceFlags as S


class Attribute(object):
  def __init__(self, script=None, **kwargs):
    if script:
      self.read(script)
    for kw in kwargs:
      setattr(self, kw, kwargs[kw])

  def __eq__(self, other):
    if isinstance(other, Attribute) and hasattr(other, 'value'):
      return self.value == other.value
    else:
      return self.value == other

  def __ne__(self, other):
    return not self.__eq__(other)

  def read(self, script, token=None, name='value'):
    if token == None:
      token = script.read_string().upper()
    if hasattr(self, token):
      setattr(self, name, getattr(self, token))
    else:
      raise ParseWarning, 'invalid parameter %s for %s' % (token.lower(), self.__class__.__name__)


# ------------------------------------------------------------------------------
# ALPHAFUNC

class AlphaFunc(Attribute):
  GT0 = (GL_GREATER, 0.0)
  LT128 = (GL_LESS, 0.5)
  GE128 = (GL_GEQUAL, 0.5)


# ------------------------------------------------------------------------------
# ALPHAGEN

class AlphaGen(Attribute):
  def __eq__(self, other):
    if not isinstance(other, AlphaGen):
      if self.mode != other.mode:
        return False
      if self.mode == AlphaGenMode.CONSTANT:
        return self.alpha == other.alpha
      elif self.mode == AlphaGenMode.PORTAL:
        return self.distance == other.distance
      elif self.mode == AlphaGenMode.WAVE:
        return self.wave == other.wave
      else:
        return True
    else:
      return NotImplemented

  def read(self, script):
    self.mode = AlphaGenMode(script)
    if self.mode == AlphaGenMode.CONSTANT:
      self.alpha = script.read_number()
    elif self.mode == AlphaGenMode.PORTAL:
      try:
        self.distance = script.read_number()
      except ParseError:
        self.distance = 256
    elif self.mode == AlphaGenMode.WAVE:
      self.wave = Wave.read(script)


class AlphaGenMode(Attribute):
  CONSTANT = 1
  ENTITY = 2
  IDENTITY = 3
  ONE_MINUS_ENTITY = 4
  ONE_MINUS_VERTEX = 5
  PORTAL = 6
  SPECULAR_LIGHTING = 7
  VERTEX = 8
  WAVE = 9

  def read(self, script):
    map = {
      'CONST': 'CONSTANT',
      'LIGHTINGSPECULAR': 'SPECULAR_LIGHTING',
      'ONEMINUSENTITY': 'ONE_MINUS_ENTITY',
      'ONEMINUSVERTEX': 'ONE_MINUS_VERTEX',
    }
    token = script.read_string().upper()
    self.value = super(AlphaGenMode, self).read(script, map.get(token, token))


# ------------------------------------------------------------------------------
# ANIMMAP

class AnimMap(Attribute):
  def __eq__(self, other):
    if isinstance(other, AnimMap):
      if self.frequency != other.frequency:
        return False
      if len(self.filenames) != len(other.filenames):
        return False
      for i, filename in enumerate(self.filenames):
        if other.filenames[i] != filename:
          return False
      return True
    else:
      return NotImplemented

  def read(self, script):
    self.frequency = script.read_number()
    self.filenames = []
    buffer = script.read_to_eol().strip()
    if buffer:
      pattern = re.compile('''\s*([^\s]+)''')
      pos = 0
      while True:
        m = pattern.match(buffer, pos)
        if m == None:
          break
        pos = m.end()
        self.filenames.append(m.group(1))


# ------------------------------------------------------------------------------
# BLENDFUNC

class BlendFunc(Attribute):
  def read(self, script):
    self.src = script.read_string().upper()
    if self.src == 'ADD' or self.src == 'GL_ADD':
      self.src, self.dst = GL_ONE, GL_ONE
    elif self.src == 'BLEND':
      self.src, self.dst = GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
    elif self.src == 'FILTER':
      self.src, self.dst = GL_DST_COLOR, GL_ZERO
    else:
      self.dst = script.read_string().upper()
      src = ['GL_ONE', 'GL_ZERO', 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA', 'GL_DST_ALPHA', 'GL_ONE_MINUS_DST_ALPHA', 'GL_DST_COLOR', 'GL_ONE_MINUS_DST_COLOR', 'GL_SRC_ALPHA_SATURATE']
      dst = ['GL_ONE', 'GL_ZERO', 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA', 'GL_DST_ALPHA', 'GL_ONE_MINUS_DST_ALPHA', 'GL_SRC_COLOR', 'GL_ONE_MINUS_SRC_COLOR']
      if not self.src in src:
        raise ParseWarning, 'invalid source blend %s' % self.src
      if not self.dst in dst:
        raise ParseWarning, 'invalid destination blend %s' % self.dst
      self.src = globals()[self.src]
      self.dst = globals()[self.dst]


# ------------------------------------------------------------------------------
# CULL

class CullSide(Attribute):
  BACK = GL_BACK
  FRONT = GL_FRONT
  FRONT_AND_BACK = GL_FRONT_AND_BACK

  def read(self, script):
    token = script.read_string().upper()
    if token in ('DISABLE', 'NONE', 'TWOSIDED'):
      self.value = None
    else:
      if token in ('BACKSIDE', 'BACKSIDED'):
        token = 'BACK'
      elif token in ('FRONTSIDE', 'FRONTSIDED'):
        token = 'FRONT'
      self.value = super(CullSide, self).read(script, token)


# ------------------------------------------------------------------------------
# DEFORMVERTEXES

class DeformMode(Attribute):
  WAVE = 1
  NORMAL = 2
  BULGE = 3
  MOVE = 4
  AUTOSPRITE = 5
  AUTOSPRITE2 = 6


class Deform(Attribute):
  pass


class AutoSpriteDeform(Deform):
  def __eq__(self, other):
    return isinstance(other, AutoSpriteDeform)

  def read(self, script):
    pass


class AutoSprite2Deform(Deform):
  def __eq__(self, other):
    return isinstance(other, AutoSprite2Deform)

  def read(self, script):
    pass


class BulgeDeform(Deform):
  def __eq__(self, other):
    if isinstance(other, BulgeDeform):
      return self.width == other.width and self.height == other.height and self.speed == other.speed
    else:
      return NotImplemented

  def read(self, script):
    self.width = script.read_number()
    self.height = script.read_number()
    self.speed = script.read_number()


class MoveDeform(Deform):
  def __eq__(self, other):
    if isinstance(other, MoveDeform):
      return self.x == other.x and self.y == other.y and self.z == other.z and other.wave == self.wave
    else:
      return NotImplemented

  def read(self, script):
    self.x = script.read_number()
    self.y = script.read_number()
    self.z = script.read_number()
    self.wave = Wave(script)


class NormalDeform(Deform):
  def __eq__(self, other):
    if isinstance(other, NormalDeform):
      return self.offset == other.offset and self.wave == other.wave
    else:
      return NotImplemented

  def read(self, script):
    self.offset = script.read_number()
    self.wave = Wave(mode=WaveMode(script), base=script.read_number(), amplitude=script.read_number(), phase=None, frequency=script.read_number())


class WaveDeform(Deform):
  def __eq__(self, other):
    if isinstance(other, WaveDeform):
      return self.offset == other.offset and self.wave == other.wave
    else:
      return NotImplemented

  def read(self, script):
    self.offset = script.read_number()
    self.wave = Wave(script)


# ------------------------------------------------------------------------------
# DEPTHFUNC

class DepthFunc(Attribute):
  ALWAYS = GL_ALWAYS
  EQUAL = GL_EQUAL
  LESS = GL_LESS
  LEQUAL = GL_LEQUAL
  GEQUAL = GL_GEQUAL
  GREATER = GL_GREATER
  NOTEQUAL = GL_NOTEQUAL
  NEVER = GL_NEVER


# ------------------------------------------------------------------------------
# FOGPARMS

class Fog(Attribute):
  def __eq__(self, other):
    if isinstance(other, Fog):
      return self.rgb == other.rgb and self.distance == other.distance
    else:
      return NotImplemented

  def read(self, script):
    self.rgb = script.read_vector()
    self.distance = script.read_number()


# ------------------------------------------------------------------------------
# MAP

class Map(Attribute):
  def read(self, script):
    self.filename = script.read_string()
    if self.filename == '$lightmap':
      self.filename, self.lightmap = None, True
    elif self.filename == '$whiteimage':
      self.filename, self.white = None, True


# ------------------------------------------------------------------------------
# RGBGEN

class RGBGen(Attribute):
  CONSTANT = 1
  DIFFUSE_LIGHTING = 2
  ENTITY = 3
  EXACT_VERTEX = 4
  IDENTITY = 5
  IDENTITY_LIGHTING = 6
  ONE_MINUS_VERTEX = 8
  VERTEX = 9
  WAVE = 10

  def __eq__(self, other):
    if isinstance(other, RGBGen):
      if self.mode != other.mode:
        return False
      if self.mode == self.CONSTANT and self.color != other.color:
        return False
      if self.mode == self.WAVE and self.wave != other.wave:
        return False
      return True
    else:
      return NotImplemented

  def read(self, script):
    token = script.read_string().upper()
    map = {
      'CONST': 'CONSTANT',
      'EXACTVERTEX': 'EXACT_VERTEX',
      'FROMVERTEX': 'VERTEX',
      'IDENTITYLIGHTING': 'IDENTITY_LIGHTING',
      'LIGHTINGDIFFUSE': 'DIFFUSE_LIGHTING',
      'ONEMINUSVERTEX': 'ONE_MINUS_VERTEX',
    }
    super(RGBGen, self).read(script, map.get(token, token), name='mode')
    if self.mode == self.CONSTANT:
      self.color = script.read_vector()
    elif self.mode == self.WAVE:
      self.wave = Wave(script)
  

# ------------------------------------------------------------------------------
# SKY

class Sky(Attribute):
  def __eq__(self, other):
    if isinstance(other, Sky):
      return self.cloud_height == other.cloud_height and self.far == other.far and self.near == other.near
    else:
      return NotImplemented

  def read(self, script):
    self.far = script.read_string()
    self.cloud_height = script.read_number()
    self.near = script.read_string()


# ------------------------------------------------------------------------------
# SORT

class Sort(Attribute):
  PORTAL = 1
  SKY = 2
  OPAQUE = 3
  DECAL = 4
  SEE_THROUGH = 5
  BANNER = 6
  UNDERWATER = 8
  ADDITIVE = 9
  NEAREST = 16

  def read(self, script):
    token_type, token = script.read()
    if token_type == 'string':
      super(Sort, self).read(script, token)
    else:
      self.value = int(token)


# ------------------------------------------------------------------------------
# SURFACEPARMS

class SurfaceParameter(Attribute):
  ALPHASHADOW = (0, S.alpha_shadow)
  AREAPORTAL = (C.area_portal, 0)
  CLUSTERPORTAL = (C.cluster_portal, 0)
  DETAIL = (C.detail, 0)
  DONOTENTER = (C.do_not_enter, 0)
  DUST = (0, S.dust)
  FLESH = (0, S.flesh)
  FOG = (C.fog, 0)
  HINT = (0, S.hint)
  LADDER = (0, S.ladder)
  LAVA = (C.lava, 0)
  LIGHTFILTER = (0, S.light_filter)
  METALSTEPS = (0, S.metal_steps)
  MONSTERCLIP = (C.monster_clip, 0)
  NODAMAGE = (0, S.no_damage)
  NODLIGHT = (0, S.no_dynamic_light)
  NODRAW = (0, S.no_draw)
  NODROP = (C.no_drop, 0)
  NOIMPACT = (0, S.no_impact)
  NOLIGHTMAP = (0, S.no_lightmap)
  NOMARKS = (0, S.no_marks)
  NOSTEPS = (0, S.no_steps)
  NONSOLID = (0, S.non_solid)
  ORIGIN = (C.origin, 0)
  PLAYERCLIP = (C.player_clip, 0)
  POINTLIGHT = (0, S.point_light)
  SKY = (0, S.sky)
  SLICK = (0, S.slick)
  SLIME = (C.slime, 0)
  STRUCTURAL = (C.structural, 0)
  TRANS = (C.translucent, 0)
  WATER = (C.water, 0)

  def read(self, script):
    super(SurfaceParameter, self).read(script)
    self.content_flags, self.surface_flags = self.value


# ------------------------------------------------------------------------------
# TCGEN

class TCGen(Attribute):
  BASE = 1
  ENVIRONMENT = 2
  LIGHTMAP = 3
  VECTOR = 4

  def __eq__(self, other):
    if isinstance(other, TCGen):
      if self.mode != other.mode:
        return False
      if self.mode == self.VECTOR and (self.s != other.s or self.t != other.t):
        return False
      return True
    else:
      return NotImplemented

  def read(self, script):
    super(TCGen, self).read(script, name='mode')
    if self.mode == self.VECTOR:
      self.s = script.read_vector()
      self.t = script.read_vector()


# ------------------------------------------------------------------------------
# TCMOD

class TCMod(Attribute):
  ROTATE = 1
  SCALE = 2
  SCROLL = 3
  STRETCH = 4
  TRANSFORM = 5
  TURB = 6

  def __eq__(self, other):
    if isinstance(other, TCMod):
      if self.mode != other.mode:
        return False
      if self.mode == self.ROTATE and self.degrees == other.degrees:
        return True
      elif (self.mode == self.SCALE or self.mode == self.SCROLL) and self.s == other.s and self.t == other.t:
        return True
      elif self.mode == self.STRETCH and self.wave == other.wave:
        return True
      elif self.mode == self.TRANSFORM and \
           self.m00 == other.m00 and self.m01 == other.m01 and \
           self.m10 == other.m10 and self.m11 == other.m11 and \
           self.t0 == other.t0 and self.t1 == other.t1:
        return True
      elif self.mode == self.TURB and \
           self.amplitude == other.amplitude and self.base == other.base and \
           self.frequency == other.frequency and self.phase == other.phase:
        return True
      else:
        return False
    else:
      return NotImplemented

  def read(self, script):
    super(TCMod, self).read(script, name='mode')
    if self.mode == self.ROTATE:
      self.degrees = script.read_number()
    elif self.mode == self.SCALE or self.mode == self.SCROLL:
      self.s = script.read_number()
      self.t = script.read_number()
    elif self.mode == self.STRETCH:
      self.wave = Wave(script)
    elif self.mode == self.TRANSFORM:
      self.m00 = script.read_number()
      self.m01 = script.read_number()
      self.m10 = script.read_number()
      self.m11 = script.read_number()
      self.t0 = script.read_number()
      self.t1 = script.read_number()
    elif self.mode == self.TURB:
      self.base = script.read_number()
      self.amplitude = script.read_number()
      self.phase = script.read_number()
      self.frequency = script.read_number()


# ------------------------------------------------------------------------------
# WAVE

class Wave(Attribute):
  def __eq__(self, other):
    if isinstance(other, Wave):
      return self.mode == other.mode and \
             self.base == other.base and self.amplitude == other.amplitude and \
             self.phase == other.phase and self.frequency == other.frequency
    else:
      return NotImplemented

  def read(self, script):
    self.mode = WaveMode(script)
    self.base = script.read_number()
    self.amplitude = script.read_number()
    self.phase = script.read_number()
    self.frequency = script.read_number()


class WaveMode(Attribute):
  SIN = 1
  SQUARE = 2
  TRIANGLE = 3
  SAW_TOOTH = 4
  INVERSE_SAW_TOOTH = 5
  NOISE = 6

  def read(self, script):
    token = script.read_string().upper()
    map = {'SAWTOOTH': 'SAW_TOOTH', 'INVERSESAWTOOTH': 'INVERSE_SAW_TOOTH'}
    super(WaveMode, self).read(script, map.get(token, token))
